from fastapi import APIRouter, Depends, UploadFile, Query, Path, Body, File, HTTPException
from fastapi.responses import JSONResponse
from sqlalchemy.orm import Session
from app.database.database import get_db
from app.services.upload_chunk_service import (
    create_upload_task, upload_chunk, complete_upload,
    get_upload_status, abort_upload, clean_expired_tasks
)
from app.database.models import User
from app.schemas.chunk_schema import (
    InitUploadRequest, InitUploadResponse, ChunkUploadRequest,
    ChunkUploadResponse, CompleteUploadRequest, UploadStatusResponse
)
from app.services.auth_service import get_current_user
from app.routers.auth_router import oauth2_scheme
from typing import Dict, Any, Optional
import logging
from app.utils.response_utils import ResponseResult

# 配置日志记录器
logger = logging.getLogger(__name__)

router = APIRouter(
    prefix="/api/chunk-upload",
    tags=["断点上传"],
    responses={
        401: {"description": "未认证"},
        403: {"description": "无权限访问"},
        404: {"description": "资源不存在"},
        500: {"description": "服务器内部错误"}
    }
)

async def get_current_user_from_token(
    token: str = Depends(oauth2_scheme),
    db: Session = Depends(get_db)
) -> User:
    """从token中获取当前用户"""
    return get_current_user(db, token)

@router.post("/init", 
    summary="初始化分片上传",
    description="创建一个新的分片上传任务，返回任务ID和相关信息。",
    response_model=Dict[str, Any]
)
async def init_chunk_upload(
    request: InitUploadRequest,
    current_user: User = Depends(get_current_user_from_token),
    db: Session = Depends(get_db)
):
    """
    初始化分片上传任务
    
    创建一个新的分片上传任务，计算所需的分片数量，生成任务ID和上传ID。
    前端应该在开始上传文件之前调用此接口。
    
    返回:
        - **task_id**: 上传任务ID
        - **upload_id**: MinIO上传ID
        - **chunk_size**: 分片大小(字节)
        - **total_chunks**: 总分片数
        - **expires_at**: 任务过期时间
    """
    logger.info(f"初始化分片上传: user_id={current_user.id}, file_name={request.file_name}, size={request.file_size}, directory_id={request.directory_id}")
    
    if request.file_size <= 0:
        raise HTTPException(status_code=400, detail="文件大小必须大于0")
    
    if request.chunk_size <= 0:
        raise HTTPException(status_code=400, detail="分片大小必须大于0")
    
    # 推荐的最小分片大小为5MB
    min_chunk_size = 5 * 1024 * 1024
    if request.chunk_size < min_chunk_size:
        logger.warning(f"分片大小过小: {request.chunk_size}字节，建议至少{min_chunk_size}字节")
    
    result = create_upload_task(
        db=db,
        user_id=current_user.id,
        file_name=request.file_name,
        file_size=request.file_size,
        chunk_size=request.chunk_size,
        mime_type=request.mime_type,
        directory_id=request.directory_id
    )
    
    return ResponseResult.success(data=result, message="初始化分片上传成功")

@router.post("/upload-chunk",
    summary="上传文件分片",
    description="上传一个文件分片。支持并发上传多个分片，每个分片需要指定任务ID和分片序号。",
    response_model=Dict[str, Any]
)
async def upload_file_chunk(
    chunk_number: int = Query(..., description="分片序号(从1开始)"),
    task_id: str = Query(..., description="上传任务ID"),
    file: UploadFile = File(..., description="分片文件内容"),
    current_user: User = Depends(get_current_user_from_token),
    db: Session = Depends(get_db)
):
    """
    上传文件分片
    
    上传单个文件分片。前端可以并发调用此接口上传多个分片，
    但需要确保每个分片的序号正确。
    
    参数:
        - **chunk_number**: 分片序号(从1开始)
        - **task_id**: 上传任务ID
        - **file**: 分片文件内容
    
    返回:
        - **task_id**: 上传任务ID
        - **chunk_number**: 分片序号
        - **etag**: 分片ETag
        - **uploaded_chunks**: 已上传分片数
        - **total_chunks**: 总分片数
    """
    logger.debug(f"上传分片: user_id={current_user.id}, task_id={task_id}, chunk={chunk_number}")
    
    if chunk_number <= 0:
        raise HTTPException(status_code=400, detail="分片序号必须大于0")
    
    # 读取分片数据
    chunk_data = await file.read()
    
    result = upload_chunk(
        db=db,
        user_id=current_user.id,
        task_id=task_id,
        chunk_number=chunk_number,
        chunk_data=chunk_data
    )
    
    return ResponseResult.success(data=result, message="分片上传成功")

@router.post("/complete",
    summary="完成上传",
    description="完成分片上传，合并所有分片为完整文件。只有所有分片都上传完成后才能调用此接口。",
    response_model=Dict[str, Any]
)
async def complete_chunk_upload(
    request: CompleteUploadRequest,
    current_user: User = Depends(get_current_user_from_token),
    db: Session = Depends(get_db)
):
    """
    完成分片上传
    
    合并所有已上传的分片，完成上传过程。
    只有在所有分片都已成功上传的情况下才能调用此接口。
    
    参数:
        - **task_id**: 上传任务ID
    
    返回:
        - **file_id**: 文件ID
        - **filename**: 存储的文件名
        - **original_filename**: 原始文件名
        - **storage_path**: 存储路径
        - **file_size**: 文件大小
        - **content_type**: 文件MIME类型
        - **file_type**: 文件类型
        - **create_time**: 文件创建时间
        - **hash**: 文件哈希值
        - **is_duplicate**: 是否为重复文件
    """
    logger.info(f"完成分片上传: user_id={current_user.id}, task_id={request.task_id}")
    
    result = complete_upload(
        db=db,
        user_id=current_user.id,
        task_id=request.task_id
    )
    
    return ResponseResult.success(data=result, message="文件上传完成")

@router.get("/status/{task_id}",
    summary="获取上传状态",
    description="获取指定上传任务的状态，包括已上传分片数、总分片数、上传进度等信息。",
    response_model=Dict[str, Any]
)
async def get_task_status(
    task_id: str = Path(..., description="上传任务ID"),
    current_user: User = Depends(get_current_user_from_token),
    db: Session = Depends(get_db)
):
    """
    获取上传任务状态
    
    查询指定上传任务的当前状态，包括整体进度和每个分片的状态。
    前端可以定期调用此接口来显示上传进度。
    
    参数:
        - **task_id**: 上传任务ID
    
    返回:
        - **task_id**: 上传任务ID
        - **file_name**: 文件名
        - **file_size**: 文件大小
        - **status**: 任务状态(pending/uploading/completed/failed)
        - **uploaded_chunks**: 已上传分片数
        - **total_chunks**: 总分片数
        - **progress**: 上传进度(0-100)
        - **chunks**: 分片状态列表
    """
    logger.debug(f"获取上传状态: user_id={current_user.id}, task_id={task_id}")
    
    result = get_upload_status(
        db=db,
        user_id=current_user.id,
        task_id=task_id
    )
    
    return ResponseResult.success(data=result, message="获取上传状态成功")

@router.post("/abort/{task_id}",
    summary="取消上传",
    description="取消指定的上传任务，释放相关资源。",
    response_model=Dict[str, Any]
)
async def abort_chunk_upload(
    task_id: str = Path(..., description="上传任务ID"),
    current_user: User = Depends(get_current_user_from_token),
    db: Session = Depends(get_db)
):
    """
    取消上传任务
    
    取消一个正在进行中的上传任务，释放服务器资源。
    已上传的分片将被清理，无法恢复。
    
    参数:
        - **task_id**: 上传任务ID
    
    返回:
        - **task_id**: 上传任务ID
        - **message**: 操作结果消息
    """
    logger.info(f"取消上传任务: user_id={current_user.id}, task_id={task_id}")
    
    result = abort_upload(
        db=db,
        user_id=current_user.id,
        task_id=task_id
    )
    
    return ResponseResult.success(data=result, message="上传任务已取消") 